﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Text;
using System.Reflection;
using System.Linq;

namespace Starehe.Testing
{
    #region Version 1.0
    /* public class GenerateData<T>
    {
        Random rnd;
      public ObservableCollection<T> GetData()
        {
            rnd = new Random();
            ObservableCollection<T> tempColl = new ObservableCollection<T>();
            int numberOfValues = rnd.Next(100, 1000);

            numberOfValues = rnd.Next(100, 1000);
            object obj;
            rnd = new Random();
            

            for (int i = 0; i < numberOfValues; i++)
            {
                obj = Activator.CreateInstance(typeof(T));
                SetPropertyValues(obj);
                tempColl.Add((T)obj);
            }
            return tempColl;
        }

        private bool IsSystemType()
        {
            Type typ = typeof(T);
            TypeCode resultTypeCode;
            return Enum.TryParse<TypeCode>(typ.Name, out resultTypeCode);

        }
        private Object GetCustomPropertyValue(PropertyInfo pi)
        {
            MessageBox.Show(pi.Name+":"+pi.GetType().FullName);
            object obj = Activator.CreateInstance(pi.GetType());
            PropertyInfo[] myPropertyInfos = pi.GetType().GetProperties();
            foreach (PropertyInfo pix in myPropertyInfos)
            {
                SetPropertyValue(obj, pi);
            }
            return obj;
        }
        private Object GetSystemPropertyValue(PropertyInfo pi)
        {
            Object propertyObjectInstance = new object();
            Type boundType = typeof(pi);
            TypeCode resultTypeCode;   
            rnd = new Random();
            if (Enum.TryParse<TypeCode>(boundType.Name, out resultTypeCode))
            {
                switch (resultTypeCode)
                {
                    case TypeCode.Boolean: { propertyObjectInstance = bool.Parse(rnd.Next(0,1).ToString()); break; }
                    case TypeCode.Byte:break; 
                    case TypeCode.Char: { propertyObjectInstance = new Char(); break; }
                    case TypeCode.DateTime:
                        {
                            propertyObjectInstance = new DateTime(rnd.Next(1901, 2012),
                               rnd.Next(1, 12), rnd.Next(1, 28)); break;
                        }
                    case TypeCode.DBNull:
                        { propertyObjectInstance = DBNull.Value; break; }
                    case TypeCode.Decimal: { break; }
                    case TypeCode.Double:
                        {
                            propertyObjectInstance = rnd.NextDouble() * 100 * rnd.NextDouble()
                                * 100 * rnd.NextDouble() * 100; break;
                        }
                    case TypeCode.Empty: { break; }
                    case TypeCode.Int16: { propertyObjectInstance = rnd.Next(0, 1000); break; }
                    case TypeCode.Int32: { propertyObjectInstance = rnd.Next(0, 10000); break; }
                    case TypeCode.Int64: { propertyObjectInstance = rnd.Next(0, 100000); break; }
                    case TypeCode.SByte: { break; }
                    case TypeCode.Single: { break; }
                    case TypeCode.String: { propertyObjectInstance = "String"+ rnd.Next(1, 1000000).ToString(); break; }
                    case TypeCode.UInt16: { break; }
                    case TypeCode.UInt32: { break; }
                    case TypeCode.UInt64: { break; }
                    case TypeCode.Object: { break; }

                    default: { break; }
                }
                return propertyObjectInstance;
            }
            return null;
        }

        private object GetGenericPropertyValue(PropertyInfo pi)
        {
            if (pi.CanWrite)
            {

                Type boundType = pi.GetType();
                if (!boundType.IsGenericType)
                { throw new ArgumentException("Not a Generic Type"); }
                Type d1 = boundType.GetGenericTypeDefinition();
                Type[] typeArgs = boundType.GetGenericArguments();
                Type constructed = d1.MakeGenericType(typeArgs);
                int numberOfValues = rnd.Next(10, 1000);
                object obj = Activator.CreateInstance(constructed);
                foreach (Type typ in typeArgs)
                {

                    for (int collLength = 0; collLength < numberOfValues; collLength++)
                    {
                        if (IsSystemType())
                        {
                            object newParam = Activator.CreateInstance(typ);
                            newParam = GetSystemPropertyValue();                            
                            MethodInfo mi = constructed.GetMethod("Add");
                            mi.Invoke(obj, new object[] { newParam });
                        }
                        else
                        {
                            object newParam = Activator.CreateInstance(typ);
                            newParam = GetCustomPropertyValue(pi);
                            MethodInfo mi = constructed.GetMethod("Add");
                            mi.Invoke(obj, new object[] { newParam });
                        }
                    }

                }
                return constructed;
            }
            return null;
        }
        private void SetPropertyValue(Object objectInstance, PropertyInfo pi)
        {
            object newVal;
            Type boundType = pi.PropertyType;
            if (boundType.IsGenericType)
            {
                newVal = GetGenericPropertyValue(pi);
                pi.SetValue(objectInstance, newVal, null);
                return;
            }
            if (IsSystemType())
            {
                newVal = GetSystemPropertyValue();
                pi.SetValue(objectInstance, newVal, null);
                return;
            }

            newVal = GetCustomPropertyValue(pi);
            pi.SetValue(objectInstance, newVal, null);

        }
        private void SetPropertyValues(Object objectInstance)
        {
            PropertyInfo[] myPropertyInfos = typeof(T).GetProperties();
            foreach (PropertyInfo pi in myPropertyInfos)
            {
                if (pi.CanWrite)
                {
                    SetPropertyValue(objectInstance, pi);                    
                }
            }

        }
    }*/
    #endregion

    public static class GenerateData
    {
        static decimal maxEntries = 1000000;
        static string[] uniqueIDs = new  string[1000000];
        public static ObservableCollection<T> GetData<T>()
        {
            return GetData<T>(1000,null);
        }
        public static ObservableCollection<T> GetData<T>(decimal numberOfValues)
        {            
            return GetData<T>(numberOfValues, null);
        }
        public static ObservableCollection<T> GetData<T>(decimal numberOfValues, string uniqueField)
        {
            if ((numberOfValues < 0) || (numberOfValues > maxEntries))
                throw new ArgumentOutOfRangeException("NumberOfValues", "Accepts integer between 1 and 1,000,000 only.");
            uniqueIDs = new string[1000000];
            ObservableCollection<T> tempColl = new ObservableCollection<T>();
            Type sport = typeof(T);
            if (string.IsNullOrWhiteSpace(uniqueField))
            {
                for (decimal i = 0; i < numberOfValues; i++)
                {
                    var obj = Activator.CreateInstance(sport);
                    SetPropertyValues(obj, sport);
                    tempColl.Add((T)obj);
                }
                return tempColl;
            }
            else
            {
                PropertyInfo pi = GetUniqueField<T>(uniqueField);
                for (decimal i = 0; i < numberOfValues; i++)
                {
                    var obj = Activator.CreateInstance(sport);
                    SetPropertyValues(obj, sport,pi);
                    tempColl.Add((T)obj);
                }
                return tempColl;
            }
        }
        public static ObservableCollection<T> GetData<T>(decimal numberOfValues, string uniqueField, IProgress<ProgressReport> onProgressPercentChanged)
        {
            if ((numberOfValues < 0) || (numberOfValues > maxEntries))
                throw new ArgumentOutOfRangeException("NumberOfValues", "Accepts integer between 1 and 1,000,000 only.");
            uniqueIDs = new string[1000000];
            ObservableCollection<T> tempColl = new ObservableCollection<T>();
            Type sport = typeof(T);
            if (string.IsNullOrWhiteSpace(uniqueField))
            {
                for (decimal i = 0; i < numberOfValues; i++)
                {
                    var obj = Activator.CreateInstance(sport);
                    SetPropertyValues(obj, sport);
                    tempColl.Add((T)obj);
                    onProgressPercentChanged.Report(new ProgressReport(((i*100)/numberOfValues).ToString()));
                }
                return tempColl;
            }
            else
            {
                PropertyInfo pi = GetUniqueField<T>(uniqueField);
                for (decimal i = 0; i < numberOfValues; i++)
                {
                    var obj = Activator.CreateInstance(sport);
                    SetPropertyValues(obj, sport, pi);
                    tempColl.Add((T)obj);
                    onProgressPercentChanged.Report(new ProgressReport(((i * 100) / numberOfValues).ToString()));
                }
                return tempColl;
            }
        }
        private static PropertyInfo GetUniqueField<T>(string uniqueField)
        {
            PropertyInfo[] myPropertyInfos = typeof(T).GetProperties();
            foreach (PropertyInfo pi in myPropertyInfos)
            {
                if (pi.Name.ToUpperInvariant()==uniqueField.ToUpperInvariant())
                {
                    return pi;
                }
            }
            throw new ArgumentOutOfRangeException("UniqueField", "The specified property [" + uniqueField +
                "] does not exist in type " + typeof(T).Name);
        }

        
        private static bool IsSystemType(Type typ)
        {
            TypeCode resultTypeCode;
            return Enum.TryParse<TypeCode>(typ.Name, out resultTypeCode);

        }
        private static object GetCustomPropertyValue(Type sport)
        {            
            var obj = new Object();
            Random rnd = new Random();

            if (sport.IsArray)
            {
                obj = null;
                return obj;
            }
            else
                obj = Activator.CreateInstance(sport);
            PropertyInfo[] myPropertyInfos = sport.GetProperties();
            foreach (PropertyInfo pi in myPropertyInfos)
            {
                SetPropertyValue(obj, pi);
            }
            return obj;
        }
        private static object GetSystemPropertyValue(Type pi)
        {
            Object propertyObjectInstance;
            Type boundType = pi;
            TypeCode resultTypeCode;
            Random rnd = new Random();
            if (Enum.TryParse<TypeCode>(boundType.Name, out resultTypeCode))
            {
                switch (resultTypeCode)
                {
                    case TypeCode.Boolean: { return true; }
                    case TypeCode.Byte: { return new byte(); }
                    case TypeCode.Char: { return new Char();  }
                    case TypeCode.DateTime:
                        {
                            return new DateTime(rnd.Next(1901, 2012),
                               rnd.Next(1, 12), rnd.Next(1, 28));
                        }
                    case TypeCode.DBNull:
                        { return DBNull.Value; }
                    case TypeCode.Decimal:
                        {
                            return Math.Round(decimal.Parse((rnd.NextDouble() * 100 * rnd.NextDouble()
                                * 100 * rnd.NextDouble() * 100).ToString()));
                        }
                    case TypeCode.Double:
                        {
                            return Math.Round( rnd.NextDouble() * 100 * rnd.NextDouble()
                                * 100 * rnd.NextDouble() * 100); 
                        }
                    case TypeCode.Empty: { break; }
                    case TypeCode.Int16: { propertyObjectInstance = rnd.Next(Int16.MinValue, Int16.MaxValue); break; }
                    case TypeCode.Int32: { propertyObjectInstance = rnd.Next(Int32.MinValue, Int32.MaxValue); break; }
                    case TypeCode.Int64: { propertyObjectInstance = rnd.Next(Int32.MinValue, Int32.MaxValue); break; }
                    case TypeCode.SByte: { break; }
                    case TypeCode.Single: { break; }
                    case TypeCode.String: { return (pi.Name + rnd.Next(Int32.MinValue, Int32.MaxValue)).ToString(); }
                    case TypeCode.UInt16: { break; }
                    case TypeCode.UInt32: { break; }
                    case TypeCode.UInt64: { break; }
                    case TypeCode.Object: { break; }

                    default: { break; }
                }
            }
            return null;
        }

        private static object GetGenericPropertyValue(PropertyInfo pi)
        {
            Random rnd = new Random();
            if (pi.CanWrite)
            {

                Type boundType = pi.GetType();
                if (!boundType.IsGenericType)
                { throw new ArgumentException("Not a Generic Type"); }
                Type d1 = boundType.GetGenericTypeDefinition();
                Type[] typeArgs = boundType.GetGenericArguments();
                Type constructed = d1.MakeGenericType(typeArgs);
                decimal numberOfValues = rnd.Next(10, 1000);
                var obj = Activator.CreateInstance(constructed);
                foreach (Type typ in typeArgs)
                {

                    for (decimal collLength = 0; collLength < numberOfValues; collLength++)
                    {
                        if (IsSystemType(typ))
                        {
                            var newParam = Activator.CreateInstance(typ);
                            newParam = GetSystemPropertyValue(typ);
                            MethodInfo mi = constructed.GetMethod("Add");
                            mi.Invoke(obj, new object[] { newParam });
                        }
                        else
                        {
                            var newParam = Activator.CreateInstance(typ);
                            newParam = GetCustomPropertyValue(typ);
                            MethodInfo mi = constructed.GetMethod("Add");
                            mi.Invoke(obj, new object[] { newParam });
                        }
                    }

                }
                return constructed;
            }
            return null;
        }
        private static void SetPropertyValue(Object objectInstance, PropertyInfo pi)
        {
            Random rnd = new Random();
            Type boundType = pi.PropertyType;

            try
            {
                if (boundType.IsGenericType)
                {
                    var newVal = Activator.CreateInstance(boundType);
                    newVal = GetGenericPropertyValue(pi);
                    pi.SetValue(objectInstance, newVal, null);
                }
                else if (IsSystemType(boundType))
                {
                    var newVal = GetSystemPropertyValue(boundType);
                    pi.SetValue(objectInstance, newVal, null);
                }
                else
                {
                    var newVal = GetCustomPropertyValue(boundType);
                    pi.SetValue(objectInstance, newVal, null);
                }
            }catch(Exception e)
            { throw new Exception(pi.Name,e); }
            

        }
        private static void SetPropertyValues(Object objectInstance, Type sport)
        {
            PropertyInfo[] myPropertyInfos = sport.GetProperties();
            foreach (PropertyInfo pi in myPropertyInfos)
            {    
            if (pi.CanWrite)            
                SetPropertyValue(objectInstance, pi);
            }

        }
        private static void SetPropertyValues(Object objectInstance, Type sport, PropertyInfo uniqueProperty)
        {            
            PropertyInfo[] myPropertyInfos = sport.GetProperties();
            foreach (PropertyInfo pi in myPropertyInfos)
            {
                if (pi.Name == uniqueProperty.Name)
                {
                    if (pi.CanWrite)
                        SetUniquePropertyValue(objectInstance, pi, ref uniqueIDs);
                }
                else if(pi.CanWrite)
                    SetPropertyValue(objectInstance, pi);
            }
        }

        private static void SetUniquePropertyValue(object objectInstance, PropertyInfo pi, ref string[] uniqueIDs)
        {
            Random rnd = new Random();
            var newVal = (string)GetSystemPropertyValue(typeof(string));
            if (uniqueIDs.Length != 0)
            {
            newItemLoop:
                bool exists = false;
                
                var query = from id in uniqueIDs.AsParallel()
                            where !string.IsNullOrWhiteSpace(id)
                            && id ==newVal
                             
                            select id;
                foreach (var o in query)
                {
                    exists = true;
                    break;
                }
                if (exists)
                    goto newItemLoop;
            }
                pi.SetValue(objectInstance, newVal, null);
        }
    }
}
